package com.asa.chart;

import com.asa.hutils.MathHUtils;

/**
 * 将三维空间上的东西投影到二维上，实现三维函数的一个展示
 * 
 * 这个东西很麻烦，理论上很好说
 * 就是三维空间到二维空间的一个投影
 * 即高等代数里面的一个同态映射
 * 
 * 
 * @author Administrator
 *
 */
public class Transform3D22D {
	
	/**
	 * 将三维坐标转成二维坐标
	 * @param xyz	三维坐标簇
	 * @param n		映射到的平面的法向量
	 * @param point	映射到的平面上的一个点	
	 * @return
	 */
	public static double[][] transform(double[][] xyz,double[] n,double[] point0){
		
		//我必须想办法把这个被映射到的平面表示出来
		//法向量！！（如果是想要和视觉上效果拟真性强，还不能单纯的这样做，需要更多的机器视觉的知识）
		//还需要一个点，这个点是确定映射平面的，要求这个点在三维函数外
		//我们还需要在二维空间中画出xyz三个轴，这三个轴是变形后的
		
		//在二维的函数中，x和y，通过小线段连接的方式来画出函数图像
		//在三维的表述中，应该是用一个个小的面块来构筑三维物体
		
		//先考虑一条线吧
		
		
		
		
		
		return null;
	}
	
	
	
	/**
	 * 求在三维空间中，一个点到一个平面上的投影点
	 * @param xyz	需要求的这个点 
	 * @param n	平面的法向量
	 * @param point0	平面上的一个点
	 * @return
	 */
	public static double[] transformpoint(double[] xyz,double[] n,double[] point0){
		
		
		double a = n[0];
		double b = n[1];
		double c = n[2];
		
		double x0 = point0[0];
		double y0 = point0[1];
		double z0 = point0[2];
		
		double x = xyz[0];
		double y = xyz[1];
		double z = xyz[2];

		
		double t = (a*x0+b*y0+c*z0-(a*x+b*y+c*z))/(Math.pow(a, 2)+Math.pow(b, 2)+Math.pow(c, 2));
		
		
		double x1 = x + a*t;
		double y1 = y + b*t;
		double z1 = z + c*t;

		double[] result = new double[3];
		result[0] = x1;
		result[1] = y1;
		result[2] = z1;

		
		return result;
	}
	
	/**
	 * 求在三维空间中，一个点到一个平面上的投影点
	 * @param xyz	需要求的这个点 
	 * @param A	平面方程参数
	 * @param B	平面方程参数
	 * @param C	平面方程参数
	 * @param D	平面方程参数
	 * @return
	 */
	public static double[] transformpoint(double[] xyz,double A,double B,double C,double D){
		
		
		
		
		double x = xyz[0];
		double y = xyz[1];
		double z = xyz[2];

		
		double t = (A*x+B*y+C*z +D)/(Math.pow(A, 2)+Math.pow(B, 2)+Math.pow(C, 2));
		
		
		double x1 = x - A*t;
		double y1 = y - B*t;
		double z1 = z - C*t;

		double[] result = new double[3];
		result[0] = x1;
		result[1] = y1;
		result[2] = z1;

		
		return result;
	}
	
	
	public static double[] transformpoint(double[] xyz,double[] xyz0,double[] xyz1,double[] xyz2){
		
		
		double x0 = xyz0[0];
		double y0 = xyz0[1];
		double z0 = xyz0[2];

		double x1 = xyz1[0];
		double y1 = xyz1[1];
		double z1 = xyz1[2];
		
		double x2 = xyz2[0];
		double y2 = xyz2[1];
		double z2 = xyz2[2];
		
		double a = (y1-y0)*(z2-z0)-(y2-y0)*(z1-z0);
		double b = (x2-x0)*(z1-z0)-(x1-x0)*(z2-z0);
		double c = (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
		
		
		
		
		
		double[] n = new double[3];
		n[0] = a;
		n[1] = b;
		n[2] = c;

		
		return transformpoint(xyz, n , xyz0);
		
	}
	
	
	/**
	 * 对空间中的点进行旋转
	 * @param xyz	传入的点
	 * @param roll	绕x轴旋转的度数
	 * @param pitch	绕y轴旋转的度数
	 * @param yaw	绕z轴旋转的度数
	 * @return
	 */
	
	public static double[] rotate(double[] xyz,double roll,double pitch,double yaw) {
		
		double[][] Rx = {{1,0,0}
			,{0,Math.cos(roll),Math.sin(roll)},
			{0,-Math.sin(roll),Math.cos(roll)}};
		
		double[][] Ry = {{Math.cos(pitch),0,-Math.sin(pitch)}
			,{0,1,0},
			{Math.sin(pitch),0,Math.cos(pitch)}};
		
		double[][] Rz = {{Math.cos(yaw),Math.sin(yaw),0}
			,{-Math.sin(yaw),Math.cos(yaw),0},
			{0,0,1}};
		
		double[][] Rxy = MathHUtils.chenfa(Rx, Ry);
		double[][] Rxyz = MathHUtils.chenfa(Rxy, Rz);

		
		
		double[][] asa = new double[1][];
		asa[0] = xyz;
		Rxyz = MathHUtils.zhuanzhi(Rxyz);
		double[][] chenfa = MathHUtils.chenfa(asa,Rxyz);
		
		for (int i = 0; i < chenfa.length; i++) {
			for (int j = 0; j < chenfa[i].length; j++) {
				System.out.println(chenfa[i][j]);
			}
		}
		
		
		
		return null;
	}
	
	
	/**
	 * 绕x轴 y轴 z轴对点进行旋转
	 * @param xyz
	 * @param rollsin
	 * @param rollcos
	 * @param pitchsin
	 * @param pitchcos
	 * @param yawsin
	 * @param yawcos
	 * @return
	 */
	public static double[] rotate(double[] xyz,double rollsin,double rollcos
			,double pitchsin,double pitchcos
			,double yawsin,double yawcos) {
		
		double[][] Rx = {{1,0,0}
			,{0,rollcos,rollsin},
			{0,-rollsin,rollcos}};
		
		double[][] Ry = {{pitchcos,0,-pitchsin}
			,{0,1,0},
			{pitchsin,0,pitchcos}};
		
		double[][] Rz = {{yawcos,yawsin,0}
			,{-yawsin,yawcos,0},
			{0,0,1}};
		
		double[][] Rxy = MathHUtils.chenfa(Rx, Ry);
		double[][] Rxyz = MathHUtils.chenfa(Rxy, Rz);
		
//		for (int i = 0; i < Rxy.length; i++) {
//			for (int j = 0; j < Rxy[i].length; j++) {
//				System.out.print(Rxy[i][j]+"  ");
//			}
//			System.out.println();
//		}
//		
//		for (int i = 0; i < Rxyz.length; i++) {
//			for (int j = 0; j < Rxyz[i].length; j++) {
//				System.out.print(Rxyz[i][j]+"  ");
//			}
//			System.out.println();
//		}
		
		
		double[][] asa = new double[1][];
		asa[0] = xyz;
//		Rxyz = MathHUtils.zhuanzhi(Rxyz);
		double[][] chenfa = MathHUtils.chenfa(asa,Rxyz);
		
//		for (int i = 0; i < chenfa.length; i++) {
//			for (int j = 0; j < chenfa[i].length; j++) {
//				System.out.println(chenfa[i][j]);
//			}
//		}
		
		
		double[] result = new double[3];
		for (int i = 0; i < Rxyz.length; i++) {
			
//			result[i] = 
			
			double[] ds = Rxyz[i];
			for (int j = 0; j < ds.length; j++) {
				result[i] += ds[j]*xyz[j];
			}

		}

		
		return result;
	}
	
	
	
	/**
	 * 将一个3D投影平面上的点，转换为一个2D的坐标
	 * 这个方法是将其转化到yz这个平面系上，画2D坐标点时，只需要取结果数组的1和2下标
	 * @param transformpoint 在投影面上的点
	 * @param x	投影面法向量
	 * @param y	投影面法向量
	 * @param z	投影面法向量
	 */
	public static double[] touying22D(double[] transformpoint, double x, double y, double z) {
		double xzhousin = z/Math.sqrt(z*z+y*y);
		double xzhoucos = -y/Math.sqrt(z*z+y*y);

		double yzhousin = z/Math.sqrt(z*z+x*x);
		double yzhoucos = x/Math.sqrt(z*z+x*x);
//		
//		double zzhousin = y/Math.sqrt(y*y+x*x);
//		double zzhoucos = x/Math.sqrt(y*y+x*x);
		
		
//		System.out.println("===========");
//		System.out.println(xzhousin);
//		System.out.println(xzhoucos);
//		System.out.println(yzhousin);
//		System.out.println(yzhoucos);
//		System.out.println("===========");

		
		
		
//		double[] rotate = rotate(transformpoint, xzhousin, xzhoucos, yzhousin, yzhoucos, zzhousin, zzhoucos);
		double[] rotate = rotate(transformpoint, xzhousin, xzhoucos, yzhousin, yzhoucos, 0, 1);
//		System.out.println("===========");
//		for (int i = 0; i < rotate.length; i++) {
//			System.out.println(rotate[i]);
//		}
//		System.out.println("===========");
		
		double[] result = new double[2];
//		result[0] = rotate[0] *Math.cos(60) + rotate[1]*Math.sin(60);
//		result[1] = rotate[1] *Math.cos(60) - rotate[0]*Math.sin(60);
		
		result[0] = rotate[0];
		result[1] = rotate[1];
		return result;
	}
	
	
	
	/**
	 * transformpoint与touying22D两个方法的融合
	 * 	这个方法直接将空间中的一个点，转化成投影到某个平面上的2d点
	 * @param point	需要转换的点
	 * @param n_fa 平面的法向量
	 * @param point0 平面上的一个点
	 * @return	double[2]	0位置的是x，1位置的是y
	 */
	public static double[] point3topoint2(double[] point,double[] n_fa, double[] point0) {
		
		return Transform3D22D.touying22D(Transform3D22D.transformpoint(point, n_fa, point0), n_fa[0], n_fa[1],
				n_fa[2]);
	}
	
	
	
	
	
	
	/**
	 * 何毅曦写的投影方法，和我上面的原理是一样的，算法公式也一样
	 * @param point
	 * @param n
	 * @param pointInPlane
	 */
	public static void transformPointByAxis(double[] point, double[] n, double[] pointInPlane) {
		double x0 = point[0];
		double y0 = point[1];
		double z0 = point[2];
		
		double d = n[0];
		double e = n[1];
		double f = n[2];		
		
		double a = pointInPlane[0];
		double b = pointInPlane[1];
		double c = pointInPlane[2];		
		
		double t = d*d + e*e + f*f;
		double diff = a*d + e*b + c*f;
		
		double x = (diff*d+(e*e+f*f)*x0-d*e*y0-d*f*z0) / t;
		
		double y = (diff*e+(d*d+f*f)*y0-d*e*x0-f*e*z0) / t;
		
		double z = (diff*f+(e*e+d*d)*z0-d*f*x0-e*f*y0) / t;
		
		System.out.println(x);
		System.out.println(y);
		System.out.println(z);
		
	}

	
	
	
	
	public static void main(String[] args) {
//		double[][] xyz = new double[2][3];
//		
//		xyz[0] = new double[]{3.0,3.0,3.0};
//		xyz[1] = new double[]{4.0,5.0,6.0};
//
//		
//		
//		for (int i = 0; i < xyz.length; i++) {
//			for (int j = 0; j < xyz[i].length; j++) {
//					System.out.print(xyz[i][j]+" ");
//			}
//			System.out.println();
//		}
		
//		double[] xyz = {3,3,3};
//		double[] n = {0,0,1};
//		double[] point0 = {1,1,0};

		double[] xyz = {0,0,0};
		double[] n = {1,1,1};
		double[] point0 = {10,10,10};
		
		
		double[] transformpoint = transformpoint(xyz, n, point0);
		
		
		for (int i = 0; i < transformpoint.length; i++) {
			System.out.println(transformpoint[i]);
		}
		
		
		
//		rotate(new double[]{1,3,2}, Math.PI/2, 0, 0);
		
		
		double x = n[0];
		double y = n[1];
		double z = n[2];
		
		touying22D(transformpoint, x, y, z);
		
		System.out.println("================");
//		transformPointByAxis(xyz, n, point0);
		
		
		
	}



	/**
	 * Perspective Transformation	透视变换矩阵
	 * 
	 * 默认眼睛是z=0
	 * @param f		眼睛到视觉平面的距离最远处
	 * @param n		眼睛到视觉平面的距离最近处（只有在n和f之间的东西会被看到）
	 * @param r		y视觉平面的极大
	 * @param l		y视觉平面的极小
	 * @param b		x视觉平面的极大
	 * @param t		x视觉平面的极小
	 * @return
	 */
	public static double[][] PerspectiveTransformation(double f,double n,double r,double l,
			double b,double t){
		
		
		
		double A = (f+n)/(f-n);
		double B = 2*f*n/(f-n);
		double result[][] = {{2*n/(r-1),0,0,0},
				{0,2*n/(t-b),0,0},
				{(r+1)/(r-1),(t+b)/(t-b),-A,-1},
				{0,0,- B,0}};
		
		
		//第二种形式的，推导过程不一样，所以结果也不一样，用法是一样的
//		double result2[][] = {{2*n/(r-1),0,(l+r)/(l-r),0},
//				{0,2*n/(t-b),(b+t)/(b-t),0},
//				{0,0,A,-1},
//				{0,0,B,0}};
		
		
		
		return result;
		
		
		
		
		
	}
	
	/**
	 * 正交变换Orthographic Transformation
	 * 
	 * 
	  * @param f		眼睛到视觉平面的距离最远处
	 * @param n		眼睛到视觉平面的距离最近处（只有在n和f之间的东西会被看到）
	 * @param r		y视觉平面的极大
	 * @param l		y视觉平面的极小
	 * @param b		x视觉平面的极大
	 * @param t		x视觉平面的极小
	 */
	
	public static double[][] OrthographicTransformation(double f,double n,double r,double l,
			double b,double t){
		
		
		
		double result[][] = {{2/(r-l),0,0,-(r+l)/(r-l)},
				{0,2/(t-b),0,-(t+b)/(t-b)},
				{0,0,-2/(f-n),-(f+n)/(f-n)},
				{0,0,0,1}};
		
		
		return result;
		
		
		
	}
	
	
	
	/**
	 * 用视度来算
	 * @param f
	 * @param n
	 * @param farfov
	 * @param aspectRatio
	 * @return
	 */
	public static double[][] OrthographicTransformation(double f,double n,double farfov,double aspectRatio){
		double t = Math.tan(farfov)/2 * n;
		double b = -t;
		double r = t * aspectRatio;
		double l = -t * aspectRatio;
		
		double result[][] = {{2/(r-l),0,0,-(r+l)/(r-l)},
				{0,2/(t-b),0,-(t+b)/(t-b)},
				{0,0,-2/(f-n),-(f+n)/(f-n)},
				{0,0,0,1}};
		
		
		return result;
		
		
		
	}
	
	
	
	

	/**
	 * Screen Space	屏幕空间
	 * @param w	频幕的宽
	 * @param h	屏幕的高
	 * @return
	 */
	public static double[][] ScreenSpace(double w,double h){
		
		
		
		double result[][] = {{w/2,0,0,w/2},
				{0,h/2,0,h/2},
				{0,0,1.0/2.0,1.0/2.0},
				{0,0,0,1.0}};
		
		
		return result;
		
		
		
	}
	
	
	
	/**
	 * ndc空间到屏幕，如果屏幕不是长宽一样，存在缩放问题
	 * @param w
	 * @param h
	 * @return
	 */
	public static double aspect(double w,double h) {
		return w/h;
		
		
	}
	
	
	/**
	 * 线性深度
	 * @param far
	 * @param near
	 * @param z
	 * @return
	 */
	public static double FdepthLinear(double far,double near,double z) {

		double result = (z -near)/(far - near);
		return result;
		
	}
	
	
	/**
	 * 非线性深度
	 * @return
	 */
	public static double Fdepth(double far,double near,double z) {
		
		
		double result = (1/z -1/near)/(1/far - 1/near);
		return result;
		
	}
	
	
	
	
	

}
